﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using SpotItComponents;

using Vitruvian.Serialization;
using Vitruvian.Serialization.Xml;
using Vitruvian.Services;

using Vitruvian.Logging;
using Vitruvian.Testing;

namespace SpotItServerAPI
{
    [ContainsTests("API.SessionManager")]
    [OptimisticSerialization]
    public class SessionManager : IAsyncRunnable
    {
        #region Private Data Members
        private static Logger logger = Logger.GetLogger(typeof(SessionManager));

        private object myLock = new object();
        private List<Session> currentSessions;
        private Guid id = Guid.NewGuid();
        private string name = "Session Manager";

        private Thread myThread = null;
        private bool running = false;
        private int cleanupFrequency = 60000;
        private int maxInactiveTime = 600000;

        private AsyncResult myAsyncResult;
        #endregion

        #region Constructor
        public SessionManager()
        {
            logger = Logger.GetLogger(typeof(SymbolManager));
        }
        #endregion

        #region Public Properties and Methods

        public int CleanupFrequency
        {
            get { return cleanupFrequency; }
            set { cleanupFrequency = value; }
        }

        public int MaxInactiveTime
        {
            get { return maxInactiveTime; }
            set { maxInactiveTime = value; }
        }

        public Session CreateSession(UserAccount userAccount)
        {
            logger.Debug("In CreateSession");

            Session newSession = null;
            lock (myLock)
            {
                Guid key = Guid.NewGuid();

                logger.DebugFormat("Create new session, key={0}, username={1}", key.ToString(), userAccount.Username);
                newSession = new Session(key, userAccount);

                logger.Debug("Add session to the list of new session");
                currentSessions.Add(newSession);
            }
            return newSession;
        }

        public Session CreateSession(string playerName)
        {
            logger.Debug("In CreateSession");

            Session newSession = null;
            if (!string.IsNullOrEmpty(playerName))
            {
                lock (myLock)
                {
                    Guid key = Guid.NewGuid();

                    logger.DebugFormat("Create new session, key={0}, username={1}", key.ToString(), playerName);
                    newSession = new Session(key, playerName);

                    logger.Debug("Add session to the list of new session");
                    currentSessions.Add(newSession);
                }
            }
            return newSession;
        }

        public bool RemoveSession(Session session)
        {
            bool result = false;

            if (session != null)
            {
                lock (myLock)
                {
                    if (currentSessions.Contains(session))
                    {
                        currentSessions.Remove(session);
                        result = true;
                    }
                }
            }

            return result;
        }

        public Session Validate(Session session)
        {
            Session result = null;
            if (session != null)
            {
                foreach (Session s in currentSessions)
                {
                    if (session.CompareTo(s) == 0)
                    {
                        result = s;
                        break;
                    }
                }
            }
            return result;
        }

        public bool IsAdmin(Session session)
        {
            bool result = false;
            Session validatedSession = Validate(session);
            if (validatedSession != null && validatedSession.UserInfo.IsAdmin)
                result = true;

            return result;
        }

        public bool IsDesigner(Session session)
        {
            bool result = false;
            Session validatedSession = Validate(session);
            if (validatedSession != null && validatedSession.UserInfo.IsAdmin)
                result = true;

            return result;
        }

        public void Stop()
        {
            running = false;
        }

        #endregion

        #region IService Members

        public Guid Id
        {
            get { return id; }
            set { id = value; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public void Init()
        {
            lock (myLock)
            {
                currentSessions = new List<Session>();
            }
        }

        public void Cleanup()
        {
            currentSessions.Clear();
        }

        #endregion

        #region IAsyncRunnable Members

        public IAsyncResult BeginRun(AsyncCallback callback)
        {
            myAsyncResult = new AsyncResult(callback);

            running = true;
            myThread = new Thread(new ThreadStart(Run));
            myThread.Start();

            return myAsyncResult;
        }

        public void EndRun(IAsyncResult result)
        {
            if (myThread!=null)
            {
                running = false;
                myThread.Join(1000);
            }
        }

        #endregion

        #region Private Methods

        private void Run()
        {
            while (running)
            {
                // Sleep for the amount of time specified by the cleanup frequence, but not in a blocked state
                int remainingTime = cleanupFrequency;
                while (remainingTime > 0 && running)
                {
                    Thread.Sleep(100);
                    remainingTime -= 100;
                }

                if (running)
                    RemoveInactiveSession();
            }
            myAsyncResult.AsyncState = null;
        }

        private void RemoveInactiveSession()
        {
            lock (myLock)
            {
                List<Session> newCurrentSessions = new List<Session>(currentSessions.Count);
                foreach (Session s in currentSessions)
                {
                    TimeSpan span = s.InactiveTimeSpan;
                    if (span.TotalMilliseconds < MaxInactiveTime)
                        newCurrentSessions.Add(s);
                }
                currentSessions = newCurrentSessions;
            }

        }

        #endregion
    }
}
